#include "HiVideoEncoder.h"

#include <string>
#include <stdio.h>


#include "hi_mapi_venc.h"
#include "hi_mapi_vproc.h"
#include "hi_mapi_sys.h"

#include "HiMediaSystem.h"

#define FRAME_COUNT -1

static HI_BOOL gbFirstPlay = HI_TRUE;

HI_S32 PFN_VENC_DataSave(HI_HANDLE vencHdl, HI_MAPI_VENC_DATA_S* pVStreamData, HI_VOID *pPrivateData)
{
    HI_S32 ret = HI_SUCCESS;
    HI_U32 i = 0;
    HI_CHAR *pFileName = (HI_CHAR *)pPrivateData;
    FILE* pVencFile = NULL;

    if (pVStreamData->u32Seq == 0) {
        if(gbFirstPlay == HI_TRUE) {
            pVencFile = fopen(pFileName, "w+");
            gbFirstPlay = HI_FALSE;
        }
        else {
            pVencFile = fopen(pFileName, "a+");
        }
    }
    else {
        pVencFile = fopen(pFileName, "a+");
    }

    for (i = 0; i < pVStreamData->u32PackCount; i++) {
        HI_U64 u64PackPhyAddr = pVStreamData->astPack[i].au64PhyAddr[0];
        HI_U32 u32PackLen = pVStreamData->astPack[i].au32Len[0];
        HI_U32 u32PackOffset = pVStreamData->astPack[i].u32Offset;

        HI_U64 s_pDataVirtAddr = (HI_U64)(HI_UL)pVStreamData->astPack[i].apu8Addr[1];
        HI_U64 s_u64DataPhyAddr = pVStreamData->astPack[i].au64PhyAddr[1];
        HI_U32 s_u32DataLen = pVStreamData->astPack[i].au32Len[1];
        HI_VOID *pPackVirtAddr = pVStreamData->astPack[i].apu8Addr[0];

        if (u64PackPhyAddr + u32PackLen >= s_u64DataPhyAddr + s_u32DataLen) {
            /* physical address retrace in offset segment */
            if (u64PackPhyAddr + u32PackOffset >= s_u64DataPhyAddr + s_u32DataLen) {
                HI_VOID *pSrcVirtAddr = (HI_VOID *)(HI_UL)(s_pDataVirtAddr +
                    ((u64PackPhyAddr + u32PackOffset) - (s_u64DataPhyAddr + s_u32DataLen)));
                ret = fwrite(pSrcVirtAddr, u32PackLen - u32PackOffset, 1, pVencFile);
                if (ret < 0) {
                    printf("fwrite error %d\n", ret);
                }
            } else {
                /* physical address retrace in data segment */
                HI_U32 u32Left = (s_u64DataPhyAddr + s_u32DataLen) - u64PackPhyAddr;
                ret = fwrite((HI_VOID *)((HI_UL)pPackVirtAddr + u32PackOffset), u32Left - u32PackOffset, 1,
                    pVencFile);
                if (ret < 0) {
                    printf("fwrite error %d\n", ret);
                }
                ret = fwrite((HI_VOID *)(HI_UL)s_pDataVirtAddr, u32PackLen - u32Left, 1, pVencFile);
                if (ret < 0) {
                    printf("fwrite error %d\n", ret);
                }
            }
        }
        else {
            /* physical address retrace does not happen */
            ret = fwrite((HI_VOID *)((HI_UL)pPackVirtAddr + u32PackOffset), u32PackLen - u32PackOffset, 1,
                pVencFile);

            if (ret < 0) {
                printf("fwrite error %d\n", ret);
            }
        }

        if (fflush(pVencFile)) {
            perror("fflush file\n");
        }
    }

    if (fclose(pVencFile)) {
        printf("fclose error\n");
    }

    if(ret >= 0)
        return HI_SUCCESS;
    else
        return HI_FAILURE;

}

HiVideoEncoder::HiVideoEncoder() : mVencHdl(0), mVpssHdl(0), mIsStop(HI_TRUE)
{
}

HiVideoEncoder::~HiVideoEncoder()
{
}

HI_S32 HiVideoEncoder::Open(HI_HANDLE vencHdl, HI_U32 width, HI_U32 height, const HI_CHAR* saveFile)
{
    HI_S32 ret= HI_SUCCESS;
    HI_MAPI_VENC_ATTR_S vencAttr;
    HI_MAPI_VENC_CALLBACK_S vencCB;

    memset(&vencAttr, 0, sizeof(HI_MAPI_VENC_ATTR_S));
    memset(&vencCB, 0, sizeof(HI_MAPI_VENC_CALLBACK_S));

    vencAttr.stVencPloadTypeAttr.enType = HI_MAPI_PAYLOAD_TYPE_H264;
    vencAttr.stVencPloadTypeAttr.u32Height = height;
    vencAttr.stVencPloadTypeAttr.u32Width = width;
    vencAttr.stVencPloadTypeAttr.u32BufSize = (height*width)<<1;
    vencAttr.stVencPloadTypeAttr.u32Profile = 2;        //base profile
    vencAttr.stVencPloadTypeAttr.enSceneMode = HI_MAPI_VENC_SCENE_MODE_DV;
    vencAttr.stRcAttr.stAttrCbr.u32Gop = 30;
    vencAttr.stRcAttr.stAttrCbr.u32StatTime = 2;
    vencAttr.stRcAttr.stAttrCbr.u32SrcFrameRate = 30;
    vencAttr.stRcAttr.stAttrCbr.fr32DstFrameRate = 30;
    vencAttr.stRcAttr.stAttrCbr.u32BitRate = 10000;

    ret |= HI_MAPI_VENC_Init(vencHdl, &vencAttr);
    ret |= HI_MAPI_VENC_SetAttr(vencHdl, &vencAttr);

    vencCB.pfnDataCB = PFN_VENC_DataSave;
    vencCB.pPrivateData = (HI_VOID *)saveFile;
    ret |= HI_MAPI_VENC_RegisterCallback(vencHdl, &vencCB);

    return ret;
}

HI_S32 HiVideoEncoder::Close(HI_HANDLE vencHdl)
{
    HI_S32 ret= HI_SUCCESS;

    if(mIsStop == HI_FALSE){
        ret |= HI_MAPI_VENC_Stop(vencHdl);
    }
    ret |= HI_MAPI_VENC_Deinit(vencHdl);

    return ret;
}

HI_S32 HiVideoEncoder::Start(HI_HANDLE vencHdl)
{
    HI_S32 ret= HI_SUCCESS;

    if(mIsStop == HI_TRUE){
        mIsStop = HI_FALSE;
    }

    ret |= HI_MAPI_VENC_Start(vencHdl, FRAME_COUNT);

    return ret;
}

HI_S32 HiVideoEncoder::Stop(HI_HANDLE vencHdl)
{
    HI_S32 ret= HI_SUCCESS;

    if(mIsStop == HI_FALSE){
        mIsStop = HI_TRUE;

        ret |= HI_MAPI_VENC_Stop(vencHdl);
    }

    return ret;
}

HI_S32 HiVideoEncoder::BindVProc(HI_HANDLE vpssHdl, HI_HANDLE vPortHdl, HI_HANDLE vencHdl)
{
    HI_S32 ret = HI_SUCCESS;

    ret |= HI_MAPI_VENC_BindVProc(vpssHdl, vPortHdl, vencHdl, HI_FALSE);

    return ret;
}

HI_S32 HiVideoEncoder::UnBindVProc(HI_HANDLE vpssHdl, HI_HANDLE vPortHdl, HI_HANDLE vencHdl)
{
    HI_S32 ret= HI_SUCCESS;

    ret |= HI_MAPI_VENC_UnBindVProc(vpssHdl, vPortHdl, vencHdl, HI_FALSE);

    return ret;
}
